package com.xingchi.tornado.security.handler;

import com.xingchi.tornado.security.config.SecurityProperties;
import com.xingchi.tornado.security.constants.SecurityConstants;
import com.xingchi.tornado.security.model.IUser;
import com.xingchi.tornado.security.model.LoginData;
import com.xingchi.tornado.security.model.UnifiedAuthenticationToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.web.authentication.session.SessionAuthenticationException;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 同一登录来源session互斥策略
 *
 * @author xingchi
 * @date 2025/9/14 13:48
 */
@Slf4j
public class SameMutexSessionControlAuthenticationStrategy implements SessionAuthenticationStrategy {

    private final SessionRegistry sessionRegistry;
    private final SecurityProperties properties;
    private final FindByIndexNameSessionRepository<? extends Session> sessionRepository;

    public SameMutexSessionControlAuthenticationStrategy(SessionRegistry sessionRegistry, SecurityProperties properties, FindByIndexNameSessionRepository<? extends Session> sessionRepository) {
        this.sessionRegistry = sessionRegistry;
        this.properties = properties;
        this.sessionRepository = sessionRepository;
    }

    /**
     * 认证完成后执行
     *
     * @param authentication 完成的认证策略
     * @throws SessionAuthenticationException
     */
    @Override
    public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) throws SessionAuthenticationException {

        UnifiedAuthenticationToken authenticationToken = (UnifiedAuthenticationToken) authentication;
        LoginData<? extends IUser> loginData = ((UnifiedAuthenticationToken) authentication).getLoginData();

        SecurityProperties.SameSessionControl sameSessionControl = properties.getSameSessionControl();
        Integer count = sameSessionControl.getAllowCount();

        // 获取用户所有session中来源与当前登陆来源一直的session
        Map<String, ? extends Session> sessionMappings = sessionRepository.findByPrincipalName(String.valueOf(authenticationToken.getLoginData().getUserId()));
        List<? extends Session> currentSessionList = sessionMappings.values()
                .stream()
                // 未过期，并且不是当前session
                .filter(session -> !session.isExpired())
                // 同一个来源，并且没有过期的
                .filter(session -> session.getAttribute(SecurityConstants.LOGIN_SOURCE) != null && session.getAttribute(SecurityConstants.LOGIN_SOURCE).equals(loginData.getLoginSource()))
                .sorted(Comparator.comparing(Session::getLastAccessedTime))
                .collect(Collectors.toList());

        int size = currentSessionList.size();
        if (size < count) {
            // 登录数量小于允许数量
            return;
        }

        if (size == count) {
            // 登录数量等于允许数量
            if (currentSessionList.stream().anyMatch(session -> session.getId().equals(loginData.getSessionId()))) {
                return;
            }
        }

        List<? extends Session> sessions = currentSessionList.subList(0, size - count + 1);
        for (Session session : sessions) {
            SessionInformation sessionInformation = sessionRegistry.getSessionInformation(session.getId());
            if (sessionInformation != null) {
                // 将session标记为过期
                sessionInformation.expireNow();
            }
        }
    }
}
